package com.zimmur.app.mocolink.manage.web.controller;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONObject;
import com.zimmur.app.mocolink.domain.model.StorageFiles;
import com.zimmur.app.mocolink.domain.util.DateUtils;
import com.zimmur.app.mocolink.domain.util.MD5;
import com.zimmur.app.mocolink.manage.web.common.Constant;
import com.zimmur.app.mocolink.manage.web.common.ReturnJsonEntity;
import com.zimmur.app.mocolink.manage.web.common.StatusCodeEnum;
import com.zimmur.app.mocolink.manage.web.fdfs.FastDFSFile;
import com.zimmur.app.mocolink.manage.web.fdfs.FileContentTypeUtil;
import com.zimmur.app.mocolink.manage.web.shiro.token.TokenManager;
import com.zimmur.app.mocolink.manage.web.util.ImageMagick;
import com.zimmur.app.mocolink.service.IStorageFilesService;

@Controller
@RequestMapping("/restful/file")
public class UserFileController extends BaseController {
	private static Logger logger = LoggerFactory.getLogger(UserFileController.class);

	@Resource(name = "trackerClient")
	private TrackerClient trackerClient;

	@Resource
	private IStorageFilesService storageFilesService;
	@Value("#{spingMvcProperties['IMAGE.BROWEURL']}")
	private String imgUrl;
	private static String AUTHOR = "author";
	private static String FILE_NAME = "fileName";

	private static Object syncObj = new Object();
	private static Object syncLocalObj = new Object();

	@ResponseBody
	@RequestMapping(value="/uploadFile",produces=MediaType.TEXT_PLAIN_VALUE+";charset=utf-8")
	public String kindeditorUpload(@RequestParam(value = "imgFile", required = true) MultipartFile files,
			@RequestParam(value = "dir") String dir) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("error", 1);
		if (files == null) {
			map.put("message", "请选择要上传的文件");
		} else {
			FastDFSFile file = null;
			try {
				List<FastDFSFile> fileList = new ArrayList<FastDFSFile>();
				if (!files.isEmpty()) {
					file = new FastDFSFile(files.getOriginalFilename(), files.getBytes(),
							StringUtils.getFilenameExtension(files.getOriginalFilename()));
					file.setSize(files.getSize());
					fileList.add(file);
				}
				int userId = TokenManager.getUserId();
				List<StorageFiles> list = mulityFileSaveFdfs(fileList, String.valueOf(userId));
				if (list != null && list.size() > 0) {
					StorageFiles storageFiles = list.get(0);
					int id = storageFiles.getId();
					String url = imgUrl + id;
					map.put("url", url);
					map.put("error", 0);
				} else {
					map.put("message", "保存文件失败");
				}
			} catch (Exception e) {
				logger.error(e.getMessage());
			}
		}
		return JSONObject.toJSONString(map);

	}

	@ResponseBody
	@RequestMapping("/upload")
	public ReturnJsonEntity upload(@RequestParam(value = "files", required = true) MultipartFile[] files) {
		ReturnJsonEntity entity = new ReturnJsonEntity();
		StatusCodeEnum statusCodeEnum = StatusCodeEnum.CODE100000;
		try {
			if (files.length == 0) {
				entity.setMsg("upload.resources.empty");
			} else {
				List<FastDFSFile> fileList = new ArrayList<FastDFSFile>();
				for (MultipartFile partFile : files) {
					FastDFSFile file = null;
					try {
						if (!partFile.isEmpty()) {
							file = new FastDFSFile(partFile.getOriginalFilename(), partFile.getBytes(),
									StringUtils.getFilenameExtension(partFile.getOriginalFilename()));
							file.setSize(partFile.getSize());
							fileList.add(file);
						}
					} catch (Exception e) {
						logger.error(e.getMessage());
					}
				}
				if (files.length == fileList.size()) {

					int userId = TokenManager.getUserId();
					try {
						List<StorageFiles> list = mulityFileSaveFdfs(fileList, String.valueOf(userId));
						if (list != null) {
							entity.setData(list);
							statusCodeEnum = StatusCodeEnum.CODE000000;
						}
					} catch (Exception er) {
						logger.error(er.getMessage());
					}
				}
			}
		} catch (Exception e) {
			statusCodeEnum = StatusCodeEnum.CODE100000;
			entity.setData(new ArrayList<>());
		}
		entity.init(statusCodeEnum);
		return entity;

	}

	@RequestMapping(value = "/download", method = RequestMethod.GET)
	public void downloadFile(HttpServletRequest request, HttpServletResponse response,
			@RequestParam(value = "fileId", required = true) String fileIdStr) {

		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {

			int fileId = 0;
			if (!StringUtils.isEmpty(fileIdStr)) {
				fileId = Integer.valueOf(fileIdStr);
			}
			StorageFiles storageFile = getStorageFile(fileId);
			if (storageFile != null) {
				response.setContentType(FileContentTypeUtil.getcontentType(storageFile.getExt()));
				InputStream sbs = new ByteArrayInputStream(
						downloadFdfsFile(storageFile.getStorageGroup(), storageFile.getStorageId()));
				bis = new BufferedInputStream(sbs);
				bos = new BufferedOutputStream(response.getOutputStream());
				byte[] buff = new byte[2048];
				int bytesRead;
				while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
					bos.write(buff, 0, bytesRead);
				}
				bos.flush();
				sbs.close();
			}

		} catch (Exception e) {
		} finally {
			try {
				if (bis != null)
					bis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if (bos != null)
					bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	@ResponseBody
	@RequestMapping(value = "/downloadthumb", method = RequestMethod.GET)
	public void downloadFileForSize(@RequestParam(value = "fileId", required = true) String fileIdStr) {
		HttpServletResponse response = getResponse();
		HttpServletRequest request = getRequest();

		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			response.setContentType("text/html;charset=UTF-8");
			request.setCharacterEncoding("UTF-8");
			int fileId = 0;
			if (!StringUtils.isEmpty(fileIdStr)) {
				fileId = Integer.valueOf(fileIdStr);
			}
			StorageFiles storageFile = getStorageFile(fileId);
			if (storageFile != null) {
				long fileLength = storageFile.getSize();
				int width = getIntParam("w");
				int height = getIntParam("h");
				String fileName = fileId + "wh." + storageFile.getExt();
				response.setContentType(FileContentTypeUtil.getcontentType(storageFile.getExt()));
				response.setHeader("Content-disposition",
						"attachment; filename=" + new String(fileName.getBytes("utf-8"), "ISO8859-1"));

				InputStream sbs = null;

				if (width > 0 || height > 0) {
					if (width > 0)
						width = (int) (width * 1.5);
					if (height > 0) {
						height = (int) (height * 1.5);
					}
					String fileFullName;
					fileFullName = checkExistsFile(storageFile.getStorageGroup(), storageFile.getStorageId(),
							storageFile.getExt(), width, height);
					if (fileFullName == null) {
						byte[] fileData = downloadFdfsFile(storageFile.getStorageGroup(), storageFile.getStorageId());
						if (saveThumbImage(storageFile.getStorageGroup(), storageFile.getStorageId(),
								storageFile.getExt(), width, height, fileData)) {
							fileFullName = checkExistsFile(storageFile.getStorageGroup(), storageFile.getStorageId(),
									storageFile.getExt(), width, height);
						}
						fileData = null;
					}
					downloadLocal(response, fileFullName, storageFile.getExt());
					return;

				} else {
					//response.setHeader("Content-Length", String.valueOf(fileLength));
					sbs = new ByteArrayInputStream(
							downloadFdfsFile(storageFile.getStorageGroup(), storageFile.getStorageId()));
					bis = new BufferedInputStream(sbs);
					bos = new BufferedOutputStream(response.getOutputStream());
					byte[] buff = new byte[2048];
					int bytesRead;
					while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
						bos.write(buff, 0, bytesRead);
					}
					bos.flush();
					sbs.close();
				}

			}

		} catch (Exception e) {
			logger.error(e.getMessage());
		} finally {
			try {
				if (bis != null)
					bis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if (bos != null)
					bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}

	}

	public void downloadLocal(HttpServletResponse response, String fileName, String fileExt)
			throws FileNotFoundException {
		OutputStream toClient = null;
		InputStream fis = null;
		try {
			synchronized (syncLocalObj) {
				// path是指欲下载的文件的路径。
				File file = new File(fileName);
				if (!file.exists()) {
					return;
				}
				// 取得文件名。
				String filename = file.getName();
				// 取得文件的后缀名。
				String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();

				// 以流的形式下载文件。
				fis = new BufferedInputStream(new FileInputStream(fileName));
				byte[] buffer = new byte[fis.available()];
				fis.read(buffer);
				fis.close();
				// 清空response
				response.reset();
				// 设置response的Header
				response.addHeader("Content-Length", "" + file.length());
				toClient = new BufferedOutputStream(response.getOutputStream());
				toClient.write(buffer);
				toClient.flush();
			}

		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (toClient != null) {
				try {
					toClient.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}
	}

	/**
	 * 检查本地文件是否存在
	 * 
	 * @param groupName
	 * @param storageId
	 * @param fileExt
	 * @param width
	 * @param height
	 * @return
	 */
	private String checkExistsFile(String groupName, String storageId, String fileExt, int width, int height) {
		// 获取我项目中存储图片的文件夹地址 你要换成你自己的目录
		String imageFold = getRequest().getServletContext().getRealPath("/thumbimages");
		File fold = new File(imageFold);
		if (!fold.exists()) {
			fold.mkdir();
		}
		String fileName = MD5.MD5Encode(storageId) + "_" + width + "x" + height + "." + fileExt;
		String fullFileName = imageFold + File.separator + fileName;
		File fileInfo = new File(fullFileName);
		if (fileInfo.exists()) {
			return fullFileName;
		}
		return null;
	}

	private boolean saveThumbImage(String groupName, String storageId, String fileExt, int width, int height,
			byte[] data) {
		String imageFold = getRequest().getServletContext().getRealPath("/thumbimages");
		File fold = new File(imageFold);
		if (!fold.exists()) {
			fold.mkdir();
		}
		String fileName = MD5.MD5Encode(storageId) + "_" + width + "x" + height;
		String fullFileName = imageFold + File.separator + fileName + "_middle" + "." + fileExt;
		String destFileName = imageFold + File.separator + fileName + "." + fileExt;
		File fileInfo = new File(destFileName);
		if (!fileInfo.exists()) {
			synchronized (syncObj) {
				String tempFileName = imageFold + File.separator + fileName + "_test." + fileExt;
				File fileTemp = new File(tempFileName);
				if (!fileTemp.exists()) {
					ImageMagick.byte2Image(data, tempFileName);
					data = null;
				}
				InputStream is = null;
				BufferedImage buff = null;
				try {
					if (fileTemp.exists()) {
						// 通过文件名称读取
						is = new FileInputStream(tempFileName);
						buff = ImageIO.read(is);
						int srcWidth = buff.getWidth();// 得到图片的宽度
						int srcHeight = buff.getHeight();// 得到图片的高度
						is.close(); // 关闭Stream

						// ImageMagick.cutImage(width, height, tempFileName,
						// fullFileName, 1, "80");
						// 1、指定宽，高自适应，等比例缩放;
						// 2、指定高， 宽自适应，等比例缩放;
						// 3、指定最长边，短边自适应，等比例缩放;
						// 4、指定最短边，长边自适应，等比例缩放;
						// 5、指定最大宽高， 等比例缩放;
						// 6、固定宽高， 居中裁剪）
						int mode = 1;
						if (width > 0 && height > 0) {
							if (srcWidth > srcHeight) {
								mode = 2;
							} else {
								mode = 1;
							}
						} else if (width > 0) {
							mode = 1;
						} else if (height > 0) {
							mode = 2;
						}
						ImageMagick imageMagick = new ImageMagick();
						imageMagick.resize(mode, tempFileName, fullFileName, destFileName, width, height, 0, 0, "85");
						fileTemp.delete();
					}
					return true;
				} catch (Exception e) {
					logger.error(e.getMessage());
				} finally {
					if (buff != null) {
						buff = null;
					}
					if (is != null) {
						try {
							is.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			}

		}
		return false;
	}

	/**
	 * 获取Fdfs文件
	 * 
	 * @param groupName
	 * @param storageId
	 * @return
	 */
	private byte[] downloadFdfsFile(String groupName, String storageId) {
		byte[] b = null;
		try {
			StorageServer storageServer = null;
			TrackerServer trackerServer = trackerClient.getConnection();
			try {

				StorageClient storageClient = new StorageClient(trackerServer, storageServer);
				b = storageClient.download_file(groupName, storageId);

			} finally {
				if (storageServer != null)
					storageServer.close();
				trackerServer.close();
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return b;
	}

	private List<StorageFiles> mulityFileSaveFdfs(List<FastDFSFile> fileList, String userId) {
		if (fileList == null || fileList.size() == 0) {
			return null;
		}
		TrackerServer trackerServer = null;
		StorageClient storageClient;
		StorageServer storageServer = null;
		try {
			trackerServer = trackerClient.getConnection();
			storageClient = new StorageClient(trackerServer, storageServer);

			if (trackerServer == null || storageClient == null) {
				logger.error("TrackerServer can not connect!");
				return null;
			}

			List<StorageFiles> storageFiles = new ArrayList<StorageFiles>();

			for (FastDFSFile fastDFSFile : fileList) {
				if (!StringUtils.isEmpty(fastDFSFile.getExt())) {
					NameValuePair nvp[] = new NameValuePair[] { new NameValuePair(AUTHOR, userId),
							new NameValuePair(FILE_NAME, fastDFSFile.getName()) };
					try {
						String[] uploadResults = storageClient.upload_file(fastDFSFile.getContent(),
								fastDFSFile.getExt(), nvp);
						if (uploadResults != null) {
							String groupName = uploadResults[0];
							String remoteFileName = uploadResults[1];

							StorageFiles storageFile = new StorageFiles();
							InputStream inputStream = null;
							try {
								inputStream = new ByteArrayInputStream(fastDFSFile.getContent());
								BufferedImage bufferedImage = ImageIO.read(inputStream);
								if (bufferedImage != null) {
									storageFile.setWidth(bufferedImage.getWidth());
									storageFile.setHeight(bufferedImage.getHeight());
								}
								inputStream.close();
							} catch (Exception e) {
								logger.error(e.getMessage());
							} finally {
								if (inputStream != null)
									inputStream.close();
							}
							storageFile.setType(Constant.STORAGE_FILE_TYPE_ADMIN);
							storageFile.setOriginalFileName(fastDFSFile.getName());
							storageFile.setSize(fastDFSFile.getSize());
							storageFile.setCreator(userId);
							storageFile.setExt(fastDFSFile.getExt());
							storageFile.setStorageGroup(groupName);
							storageFile.setStorageId(remoteFileName);
							storageFile.setCreateTime(DateUtils.getNow());
							storageFiles.add(storageFile);
						}
					} catch (Exception e) {
						logger.error(e.getMessage(), e);
						return null;
					}
				}
			}
			if (storageFiles.size() > 0) {
				saveStorageToDb(storageFiles);
				return storageFiles;
			} else {
				return null;
			}
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
			return null;
		} finally {
			if (trackerServer != null)
				try {
					trackerServer.close();
				} catch (IOException e) {
					logger.error("trackerServer close error,message:{},info:{}", e.getMessage(), e);
				}
		}
	}

	private StorageFiles getStorageFile(int fileId) {
		return storageFilesService.selectByFileId(fileId);
	}

	/**
	 * 保存上传数据信息
	 * 
	 * @param files
	 * @return
	 */
	private boolean saveStorageToDb(List<StorageFiles> files) {
		boolean bRet = true;
		try {
			for (StorageFiles storageFiles : files) {
				storageFiles.setId(null);
				storageFilesService.add(storageFiles);
			}
		} catch (Exception e) {
			bRet = false;
		}
		return bRet;
	}
}
